# Объект Future часть 2
# Состояния объектов Future/Task. Методы done(), cancel(), cancelled().
# Объект Future/Task может находиться в одном из трёх состояний:
# Pending (ожидание) -  в этом состоянии операция, связанная с объектом Future , еще не выполнена.
# Это начальное состояние для всех объектов Future/Task. При попытке получить результат операции, 
# будет вызвано исключение InvalidStateError. 
# import asyncio

# async def main():
#     future = asyncio.Future()
#     if not future.done():
#         print("Состояние: Pending (ожидание)")
#     try:
#         result = future.result()
#         print(result)
#     except asyncio.InvalidStateError:
#         print('Задача еще не выполнена. Доступа к результатам нет!')

# asyncio.run(main())

# Completed (завершён) - когда операция завершена, объект Future переходит в состояние "завершено". В этом состоянии вы можете получить
# результат операции, вызвав метод result() объекта Future. Если операция завершилась с ошибкой, вызов метода result() приведет к 
# возникновению исключения. Подробнее об исключениях будем говорить в следующих разделах курса. 
# import asyncio

# async def main():
#     future = asyncio.Future()
#     future.set_result('Задача завершена')
#     result = future.result()

#     if future.done():
#         print("Состояние: Completed (завершено)")
#         print("Результат:", result)


# asyncio.run(main())

# Что такое done()
# done() является методом класса asyncio.Future библиотеки asyncio Python. Также доступен для объектов asyncio.Task.
# Этот метод проверяет, завершился ли объект Future и возвращает True, если объект Future завершен.
# Объект Future считается завершенным, если он был отменен или если для него был установлен результат или исключение
# с помощью вызовов set_result() или set_exception().
# Если Future еще не завершен, метод done() возвращает False.
# Этот метод полезен для проверки состояния объекта Future без блокирования его выполнения и без извлечения результата,
# который может вызвать исключение, если Future завершился с ошибкой. 


# Cancelled (отменён) - если операция была отменена, объект Future переходит в состояние "отменено". Операция может быть
# отменена вызовом метода cancel() объекта Future. Для проверки отмены операции можно использовать метод future.cancelled().
# Отменить можно только незавершенную задачу. Если операция была отменена, вызов result() вызовет исключение CancelledError. 

# import asyncio


# async def main():
#     future = asyncio.Future()
#     future.cancel()
#     if future.cancelled():
#         print("Состояние: Cancelled (отменено)")
#     try:
#         result = future.result()
#         print(result)
#     except asyncio.CancelledError:
#         print('Задача отменена. Доступа к результатам нет!')

# asyncio.run(main())

# Вывод:

# Состояние: Cancelled (отменено)
# Задача отменена. Доступа к результатам нет!
# Все статусы и методы для проверки состояния статусов, приведенные выше для объектов Future применимы и для объектовTask.


#######################################

# Метод asyncio.ensure_future().
# До Python 3.7 для создания объектов Future использовался метод asyncio.ensure_future(). 

# Важно знать!
# asyncio.ensure_future() —  функция доступна начиная с Python 3.4 и немного устарела.
# asyncio.create_task() — функция была введена в Python 3.7 и стала предпочтительным способом создания задач.

# На текущий момент оба метода, asyncio.ensure_future() и asyncio.create_task(), при передаче в них корутин или задач, 
# в конечном итоге возвращают объект Task. Легко проверить что задачи, созданные при помощи .ensure_future() и .create_task(),
# имеют одинаковый type,  <class '_asyncio.Task'>. 

import asyncio


async def my_coroutine():
    await asyncio.sleep(1)
    return 'Задача завершена'

async def main():
    task1 = asyncio.ensure_future(my_coroutine())
    task2 = asyncio.create_task(my_coroutine())
    print(f'Тип объекта, созданного методом ensure_future(): {type(task1)}')
    print(f'Тип объекта, созданного методом create_task(): {type(task2)}')
    await asyncio.gather(task1, task2)

asyncio.run(main())

# Вывод:

# Тип объекта, созданного методом ensure_future(): <class '_asyncio.Task'>
# Тип объекта, созданного методом create_task(): <class '_asyncio.Task'>

# Почему оба объекта имеют тип _asyncio.Task, а не просто asyncio.Task? Это связано с тем, что _asyncio.Task - это внутренний класс, 
# используемый в реализации asyncio. Внешний интерфейс предоставляет вам asyncio.Task, но
# в реализации это действительно _asyncio.Task. На практике это не имеет значения для большинства пользователей, но
# может вызвать некоторую путаницу при выводе типа объекта.

# Разница между методами asyncio.ensure_future() и asyncio.create_task() заключается в уровне абстракции и предпочтительном использовании. 

# asyncio.ensure_future() может принимать не только корутины или задачи, но и объект Future. В этом случае возвращается сам объект Future.

# Если у вас есть выбор между asyncio.ensure_future() и asyncio.create_task(), рекомендуется использовать asyncio.create_task(), так как это более
# современный метод для работы с корутинами.

# Использование объектов Future напрямую в коде становится все менее распространенным, так как современные версии Python предоставляют
# более удобные абстракции, такие как корутины  async def , await и объекты Task. Однако Future все еще полезен для взаимодействия
# с более низкоуровневым асинхронным кодом или сторонними библиотеками, которые используют его для представления асинхронных результатов. 